自宅の回線が時間によってめちゃくちゃ遅くなるのをMackerelとSpeedtest CLIで可視化した
結果
こうなりました(結論からさらすスタイル)。
いまの御時世の固定回線で 下り 3Mbps って何ですかね??? というのは実は本題ではなくて、このようなグラフを作ることがこの記事の本題です。
背景
状況については冒頭の概要に書いたとおりなのですが、とにかく自宅のネットワーク回線を定期的に測定して可視化することを試みました。
ちなみに我が家は古い賃貸集合住宅で VDSL なので、上限は 100Mbps になります。それでもそこそこ快適で、特に不満もなかったのですが1、今月に入った辺りから急に回線状況の悪い時間帯に出くわすようになってしまいました。
改善策をとるまえにまずは計測、ということで、今回の試みとなったわけです。
仕組み
ざっくりいうと、
- 自宅の Windows PC に Hyper-V で Linux を動作させ
- その Ubuntu に
mackerel-agent
を導入し - 10 分おきに実行した Speedtest CLI の結果を
mackerel-agent
ですくい上げて Mackerel に送信し- カスタムダッシュボードでそれっぽく表示
させてます。
まずは普段使ってる環境に近い経路での測定を行うので、あえて Wi-Fi 経由になるようにしました。
仕事用の Mac 以外に自由になる環境が Windows PC しかなかったため、その上で Hyper-V で Linux VM を動作させることにしてます。
がんばれば Windows 上で直接やれそうな手応えはあったのですが、そこをがんばる理由もなかったので、さくっと Ubuntu を導入。
回線速度の計測にはSpeedtest CLIを使っています。
最近は回線テストと言えば Google で「speedtest」と検索 するようになってしまいましたが、以前は Speedtest のお世話になっていました。このテストを CLI から実行できるということで、今回の目的にはぴったりです。
定期的に実行しても大丈夫なものか、とは思ったんですが、
Speedtest CLIを使うと、次のようなことを簡単に行うことができます。
(略)
自動化されたスクリプトをセットアップして、経時的な傾向を含む接続パフォーマンスデータを収集
との記載も Web にあり、個人が運営してるサービスでもないから大丈夫だろうと判断しました。
それでも毎分実行はさすがにやり過ぎ 2 だと思うので(自宅の回線も圧迫されますし)、10 分ごとに実行することにしています。テストのために回線が圧迫されるのも本末転倒ですからね。。
可視化には Mackerel を使います。
必要なメトリックをさくっと送信してさくっと可視化する、という要件に、さくっと対応出来るのほんと助かりますね。
Speedtest CLI で取得出来る値が Bytes per seconds になってたので、ついでに 式によるダッシュボード をつくって bps 単位で表示させています。3
なお mackerel-agent によるデータの収集は 1 分間隔固定なので、Speedtest CLI は cron で実行して結果をファイルに書き出しておき、mackerel-agent はその中身をひろうような仕組みにしています。
セットアップ
以下導入手順です。同様のことをされる方がいらしたら参考にしてください。
ちなみに Windows 10 の Hyper-V で Linux VM を建てるところは、長くなりすぎるので今回は省きます。以下が参考になると思います。
Ubuntu のインストール用 ISO は下記から入手しました。デスクトップは要らないので Ubuntu Server 20.04.3 LTS を選択しています。
Speedtest CLI
Ubuntu 用の Speedtest CLI なので、apt でさくっと導入してしまいます。
$ curl -s https://install.speedtest.net/app/cli/install.deb.sh | sudo bash $ sudo apt-get install speedtest
Speedtest CLI は初回実行時にライセンスの確認をしてくるので、cron で実行するユーザー権限(今回は root
)で実行しておきます。
そうすることで、該当ユーザのホームディレクトリの下に .config/ookla/speedtest-cli.json
というファイルが作成され、以後同じ質問をしてこなくなります。
$ sudo speedtest : Do you accept the license? [type YES to accept]: yes License acceptance recorded. Continuing. :
ちなみに、スクリプトや一時ファイルを /etc/mackerel-agent/
以下にまとめたかったので root
で実行するようにしましたが、正しく権限設計すれば root
を使わなくてもいいはずなので、よい子は真似しないでください。
jq
Speedtest CLI には、結果を JSON で出力するオプション(-f json
)があるのでそれを使います。
ということは、JSON を読んで Mackerel プラグインの形式で出力する何かが必要になるのですが、今回ここにはjq
を使いました。
というわけでインストールしておきます。
$ sudo apt-get install jq
mackerel-agent
こちらも公式ドキュメントに従って導入します。Mackerel にログインして以下の URL から、導入先の OS にあったインストラクションを選択します。
今回は Ubuntu なので、下記の様になりました。
(API キーはマスクしてます)
$ wget -q -O - https://mackerel.io/file/script/setup-all-apt-v2.sh | \ MACKEREL_APIKEY='******' sh
インストールが終わったら、自動的に mackerel-agent
サービスが起動しているはずです。
スクリプトの配置
ツールがそろったら、定期的に実行して Mackerel に投げるための仕掛けを用意します。
まずは Speedtest CLI を実行して、Mackerel プラグインのフォーマットに整形するスクリプトがこちらです。
#!/bin/bash /usr/bin/speedtest -f json -p no -s **** | \ /usr/bin/jq -r ' "speedtest.ping.jitter\t" + (.ping.jitter|tostring) + "\t" + (.timestamp|fromdate|tostring), "speedtest.ping.latency\t" + (.ping.latency|tostring) + "\t" + (.timestamp|fromdate|tostring), "speedtest.packetloss.packetloss\t" + (.packetLoss|tostring) + "\t" + (.timestamp|fromdate|tostring), "speedtest.bandwidth.download\t" + (.download.bandwidth|tostring) + "\t" + (.timestamp|fromdate|tostring), "speedtest.bandwidth.upload\t" + (.upload.bandwidth|tostring) + "\t" + (.timestamp|fromdate|tostring) '
3 行目の -s ****
ですが、スピードテストを行う時の先方のサーバを ID 指定して固定しています。
どこがいいかは設置場所に依ると思うので、まずオプションなしに speedtest
を実行して、その出力から拾ってください。
ファイルを設置したら、chmod +x
して実行権限をつけておいてください。
試しに動かして見るといいでしょう。
$ sudo /etc/mackerel-agent/mackerel-speedtest.sh
これがうまく動くようであれば、cron から定期的に実行させます。
SHELL=/bin/sh PATH=/usr/bin 3-59/10 * * * * root cd /etc/mackerel-agent && ./mackerel-speedtest.sh > mackerel-speedtest.out
書き込んだら、念のため crond に再読み込みさせましょう。
$ sudo service cron reload
うまくいけば、10 分ごと(毎時 3 分、13 分、23 分 ...)に、以下のようなファイルが生成されるかと思います。
speedtest.ping.jitter 3.256 1634110233 speedtest.ping.latency 9.312 1634110233 speedtest.ping.packetloss 0 1634110233 speedtest.bandwidth.download 10584407 1634110233 speedtest.bandwidth.upload 8132137 1634110233
そしたら、このファイルの内容を Mackerel に送信する準備をします。
まずは、Mackerel のプラグインの流儀 に従ってこのファイルを扱うための、ラッパースクリプトを作成します。
#!/bin/bash if [ "$MACKEREL_AGENT_PLUGIN_META" = "1" ]; then echo '# mackerel-agent-plugin' cat <<EOF { "graphs": { "custom.speedtest.bandwidth": { "label": "Speedtest Bandwidth (B/s)", "unit": "bytes/sec", "metrics": [ {"name": "download","label": "Download speed"}, {"name": "upload", "label": "Upload speed"} ] }, "custom.speedtest.ping": { "label": "Speedtest Network Quality (msec)", "unit": "float", "metrics": [ {"name": "latency","label": "Latency"}, {"name": "jitter", "label": "Jitter"} ] }, "custom.speedtest.packetloss": { "label": "Speedtest Network Quality PacketLoss (%)", "unit": "percentage", "metrics": [ {"name": "packetloss","label": "PacketLoss"} ] } } } EOF exit 0 else cat /etc/mackerel-agent/mackerel-speedtest.out fi
環境変数 MACKEREL_AGENT_PLUGIN_META
に 1
がセットされていたらグラフ定義の指定 (JSON) を出力し、そうでなければ /etc/mackerel-agent/mackerel-speedtest.out
の内容を返します。
こちらも、ファイルを作ったら chmod +x
しておいてください。
ちなみにmackerel-speedtest.out
には、Speedtest CLI から出力された測定時のタイムスタンプを埋め込んでいるのですが、Mackerel に送信される段階でその値は無視されて、mackerel-agent
が処理した時間のデータとして Mackerel に格納・表示されるようです。
今回の場合はそれで問題ないのでそのままにしてます。
次に、このラッパースクリプトを mackerel-agent
が呼び出すよう mackerel-agent.conf
に指定 (追記) します。
: [plugin.metrics.speedtest] command = '/etc/mackerel-agent/mackerel-speedtest-wrapper.sh'
そしたら、mackerel-agent を再起動してください。
$ sudo systemctl restart mackerel-agent.service
準備は以上です!
しばらく(数分)したら、Mackerel にグラフが出来てくるかと思います!
もし mackerel-agent.conf
に誤りがあるなどの場合は、 /var/log/syslog
に出力があると思うのでご確認下さい。
カスタムダッシュボードの作成
このままでも十分ではあったんですが、以下がちょっと引っかかりました。
- カスタムメトリックスのグラフが下の方にあって、毎回スクロールするのめんどい
- Byte 単位になっている。Bit 単位にしたい
幸いなことに、ぼくはここでカスタムダッシュボードの機能を使うことができたので、実験的機能も使いつつ改善してみました。
それがこちらです(再掲)。
先ほども申し上げましたが、下り 3Mbps って何ですかね??? (300 行ぶり 2 度目)
それは本題ではないのでおいといて、このダッシュボードを作るにあたって使用した式は下記になります。改行とインデントは可読性のために適当につけましたが、設定上はあってもなくてもいいようです。
group( alias( scale( host( <ホストID>, custom.speedtest.bandwidth.download ), 8 ), 'Download' ), alias( scale( host( <ホストID>, custom.speedtest.bandwidth.upload ), 8 ), 'Upload' ) )
- ホストメトリック
custom.speedtest.bandwidth.*
を 8 倍して Bit 値にしつつ(scale ()
) - グラフ上の表示名を
alias ()
で指定し group ()
で二つをまとめる
というのがポイントです。
ちなみに、Bandwidth グラフの右側に大きく Mbps を表示しているところの式は下記になります。
scale(host(<ホストID>,custom.speedtest.bandwidth.upload),8/1048576)
scale(host(<ホストID>,custom.speedtest.bandwidth.download),8/1048576)
まとめと結論
思わずカッとなって H○me 5G か NUR○光を契約しそうになったんですが、そこはぐっとガマンしつつ、このブログを書き終えたら続いて有線と無線の影響の切り分けに入りたいと思います。
なんか、Jitter がスパイクしたタイミングで Latency の傾向が明らかに変化したのが気になるんですよね。。
・・・あ、
そういうのが一目で分かる可視化最高って思いました!
特に、
CLIからさくっとSpeedtestが出来るサービスを提供してくださってる OOKLS さん、
そういった情報をさくっと可視化できる素晴らしいサービスを展開してくださってる はてな さんにも大感謝です。ありがとうございます!
余録 1: PowerShell 版
一瞬 Windows だけでやろうとした時に作った PowerShell を供養しておきます。一応これ単体で動きはします。
誰かの何かのお役に立てば幸いです。
Function ConvertTo-UnixEpoch { Param( $DateTime ) return ((Get-Date($DateTime)) - (Get-Date("1970/1/1 0:0:0 GMT"))).TotalSeconds } $raw = &"C:\opt\speedtest\speedtest.exe" -f json -p no $result = ConvertFrom-Json $raw $timestamp = ConvertTo-UnixEpoch $result.timestamp Write-Output ("speedtest.ping.jitter`t{0}`t{1}" -f $result.ping.jitter, $timestamp) Write-Output ("speedtest.ping.latency`t{0}`t{1}" -f $result.ping.latency, $timestamp) Write-Output ("speedtest.ping.packetloss`t{0}`t{1}" -f $result.packetloss, $timestamp) Write-Output ("speedtest.bandwidth.download`t{0}`t{1}" -f $result.download.bandwidth,$timestamp) Write-Output ("speedtest.bandwidth.upload`t{0}`t{1}" -f $result.upload.bandwidth, $timestamp)
ちなみに最終的に Linux に逃げた理由は、mackerel-agent から直接 speedtest.exe
を実行したときにライセンス同意をパスできなかったからですが、最終的な構成である「Speedtest CLI の実行を mackerel-agent と分離する」やり方であれば問題なさそうです。
余録 2: もうひとつの Speedtest CLI
Speedtest CLI には別の実装(Python 製)もあるようで、こちらであれば最初から bit/s で値がとれそうです。
でも Mackerel のグラフの「単位」として指定できるのは Bytes/s なので、まあいいかなと思います。
macOS から Homebrew で tap せずに install するとこちらが入るようです。出力される JSON のフォーマットが異なりますのでご注意を。
参考
SREであることを自覚するのは……
……最初の質問として「それをどのように計測していますか」が口をついて出る時。
謝辞
この仕組みを作るのに、以下のサイトを参考にさせて頂きました。この場を借りて感謝いたします。
- 回線速度を定期的にベンチマーク | Matsubo Tech Blog
- [PowerShell 7] ConvertFrom-Json を使用して JSON ファイルを読み込む | PowerShell from Japan!! Blog
- Powershell 日時文字列の取得、Unixエポック秒に変換、ISO8601形式に変換
- Hello, world! : PowerShellでコマンドの実行結果を取得する
- PowerShell のスクリプトが実行できない場合の対処方法 - Qiita